import * as Cesium from 'cesium';
//@ts-ignore
const { PrimitiveLoadPlan, PrimitiveOutlineGenerator, IndexDatatype, ModelComponents, ComponentDatatype, AttributeType,
    //@ts-ignore
    AttributeLoadPlan, defined, BufferUsage, Cartesian3, AttributeCompression
} = Cesium;
export default function fixPrimitiveLoadPlan() {
    Object.defineProperties(PrimitiveLoadPlan, {
        needGaussianSplats: {
            value: false
        },
        generateGaussianSplatTexture: {
            value: false
        }
    })
    PrimitiveLoadPlan.prototype.postProcess = function (context: any) {
        // Handle CESIUM_primitive_outline. This modifies indices and attributes and
        // also generates a new attribute for the outline coordinates. These steps
        // are synchronous.
        if (this.needsOutlines) {
            generateOutlines(this);
            generateBuffers(this, context);
        }

        //handle splat post-processing for point primitives
        if (this.needsGaussianSplats) {
            this.primitive.isGaussianSplatPrimitive = true;
            setupGaussianSplatBuffers(this, context);
            if (this.generateGaussianSplatTexture) {
                this.attributePlans.forEach((attr: any) => {
                    dequantizeSplatMeshopt(attr);
                    this.primitive.needsGaussianSplatTexture = true;
                });
            }
        }
    };
    function dequantizeSplatMeshopt(attribute: any) {
        if (
            attribute.name === "_ROTATION" &&
            attribute.componentDatatype === ComponentDatatype.SHORT
        ) {
            attribute.typedArray = AttributeCompression.dequantize(
                attribute.typedArray,
                ComponentDatatype.SHORT,
                AttributeType.VEC4,
                attribute.count,
            );
            attribute.componentDatatype = ComponentDatatype.FLOAT;
        }

        if (
            attribute.name === "POSITION" &&
            attribute.componentDatatype === ComponentDatatype.UNSIGNED_SHORT
        ) {
            const fa = Float32Array.from(
                attribute.typedArray,
                (n) => n / attribute.max.x,
            );
            attribute.typedArray = fa;
            attribute.componentDatatype = ComponentDatatype.FLOAT;
            attribute.normalized = false;
            attribute.constant = new Cartesian3(0, 0, 0);

            const findMinMaxXY = (flatArray: any) => {
                let minX = Infinity;
                let maxX = -Infinity;
                let minY = Infinity;
                let maxY = -Infinity;
                let minZ = Infinity;
                let maxZ = -Infinity;

                for (let i = 0; i < flatArray.length; i += 3) {
                    const x = flatArray[i];
                    const y = flatArray[i + 1];
                    const z = flatArray[i + 2];

                    minX = Math.min(minX, x);
                    maxX = Math.max(maxX, x);
                    minY = Math.min(minY, y);
                    maxY = Math.max(maxY, y);
                    minZ = Math.min(minZ, z);
                    maxZ = Math.max(maxZ, z);
                }

                return [
                    new Cartesian3(minX, minY, minZ),
                    new Cartesian3(maxX, maxY, maxZ),
                ];
            };
            [attribute.min, attribute.max] = findMinMaxXY(attribute.typedArray);
        }
    }

    function setupGaussianSplatBuffers(loadPlan: any, context: any) {
        const attributePlans = loadPlan.attributePlans;
        const attrLen = attributePlans.length;
        for (let i = 0; i < attrLen; i++) {
            const attributePlan = attributePlans[i];
            //defer til much later into the pipeline
            attributePlan.loadBuffer = false;
            attributePlan.loadTypedArray = true;

            const attribute = attributePlan.attribute;
            dequantizeSplatMeshopt(attribute);
        }
    }
}
//#region 原始代码
function generateOutlines(loadPlan: any) {
    const primitive = loadPlan.primitive;
    const indices = primitive.indices;

    const vertexCount = primitive.attributes[0].count;

    const generator = new PrimitiveOutlineGenerator({
        triangleIndices: indices.typedArray,
        outlineIndices: loadPlan.outlineIndices,
        originalVertexCount: vertexCount,
    });

    // The generator modifies/adds indices. In some uncommon cases it may have
    // to upgrade to 16- or 32-bit indices so the datatype may change.
    indices.typedArray = generator.updatedTriangleIndices;
    //@ts-ignore
    indices.indexDatatype = IndexDatatype.fromTypedArray(indices.typedArray);

    // The outline generator creates a new attribute for the outline coordinates
    // that are used with a lookup texture.
    const outlineCoordinates = makeOutlineCoordinatesAttribute(
        generator.outlineCoordinates,
    );
    const outlineCoordinatesPlan = new AttributeLoadPlan(outlineCoordinates);
    outlineCoordinatesPlan.loadBuffer = true;
    outlineCoordinatesPlan.loadTypedArray = false;
    loadPlan.attributePlans.push(outlineCoordinatesPlan);
    primitive.outlineCoordinates = outlineCoordinatesPlan.attribute;

    // Some vertices may be copied due to the addition of the new attribute
    // which may have multiple values at a vertex depending on the face
    const attributePlans = loadPlan.attributePlans;
    const attributesLength = loadPlan.attributePlans.length;
    for (let i = 0; i < attributesLength; i++) {
        const attribute = attributePlans[i].attribute;
        attribute.typedArray = generator.updateAttribute(attribute.typedArray);
    }
}
function makeOutlineCoordinatesAttribute(outlineCoordinatesTypedArray: any) {
    const attribute = new ModelComponents.Attribute();
    attribute.name = "_OUTLINE_COORDINATES";
    attribute.typedArray = outlineCoordinatesTypedArray;
    attribute.componentDatatype = ComponentDatatype.FLOAT;
    attribute.type = AttributeType.VEC3;
    attribute.normalized = false;
    attribute.count = outlineCoordinatesTypedArray.length / 3;

    return attribute;
}
function generateBuffers(loadPlans: any, context: any) {
    generateAttributeBuffers(loadPlans.attributePlans, context);

    if (defined(loadPlans.indicesPlan)) {
        generateIndexBuffers(loadPlans.indicesPlan, context);
    }
}
function generateAttributeBuffers(attributePlans: any, context: any) {
    const attributesLength = attributePlans.length;
    for (let i = 0; i < attributesLength; i++) {
        const attributePlan = attributePlans[i];
        const attribute = attributePlan.attribute;
        const typedArray = attribute.typedArray;

        if (attributePlan.loadBuffer) {
            //@ts-ignore
            const buffer = Buffer.createVertexBuffer({
                typedArray: typedArray,
                context: context,
                usage: BufferUsage.STATIC_DRAW,
            });
            buffer.vertexArrayDestroyable = false;
            attribute.buffer = buffer;
        }

        if (!attributePlan.loadTypedArray) {
            attribute.typedArray = undefined;
        }
    }
}

function generateIndexBuffers(indicesPlan: any, context: any) {
    const indices = indicesPlan.indices;
    if (indicesPlan.loadBuffer) {
        //@ts-ignore
        const buffer = Buffer.createIndexBuffer({
            typedArray: indices.typedArray,
            context: context,
            usage: BufferUsage.STATIC_DRAW,
            indexDatatype: indices.indexDatatype,
        });
        indices.buffer = buffer;
        buffer.vertexArrayDestroyable = false;
    }

    if (!indicesPlan.loadTypedArray) {
        indices.typedArray = undefined;
    }
}
//#endregion